//rstream.cpp based on Port Audio's pa_record test file

//include new header
#include "stdafx.h"

#include "rstream.h"

#include "portaudio.h"
#include "Spectrum.h"
#include "FFT.h"

const char *
PortAudioInit::deviceName() const
{
  const PaDeviceInfo *pdi = Pa_GetDeviceInfo(Pa_GetDefaultInputDeviceID());
  return pdi->name;
}

static int recordCallback(void *inputBuffer, void *outputBuffer,
                          unsigned long framesPerBuffer,
                          PaTimestamp outTime, void *userData )
{
  paRecData *data = (paRecData*)userData;
  float *rptr = (float*)inputBuffer;
  float *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
  long framesToCalc;
  long i, j;
  int finished;
  unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;

  (void) outputBuffer; /* Prevent unused variable warnings. */
  (void) outTime;
	
  if( framesLeft < framesPerBuffer ) {
    framesToCalc = framesLeft;
    finished = 1;
  }
  else {
    framesToCalc = framesPerBuffer;
    finished = 0;
  }

  if( inputBuffer == NULL ) {
    for( i=0; i<framesToCalc; i++ ) {
      for (j=0; j<data->samplesPerFrame; j++) {
        *wptr++ = 0;
      }
    }
  }
  else {
    for( i=0; i<framesToCalc; i++ ) {
      for (j=0; j<data->samplesPerFrame; j++) {
        *wptr++ = *rptr++;
      }
    }
  }
  data->frameIndex += framesToCalc;
  return finished;
}


recordStream::recordStream() {
  data.frameIndex = 0;
  data.samplesPerFrame = 1;    /* 1 for mono input/output, 2 for stereo. */
}

recordStream::~recordStream() {
  if (data.recordedSamples) {
    delete [] data.recordedSamples;
  }
}

bool recordStream::open(const PortAudioInit &init) {
  m_sample_rate = 22050;

  // Allow buffer space for at least this many seconds
  // If all of this fills up, the record stream will close
  const int NUM_SECONDS = 10;
  data.maxFrameIndex = int(NUM_SECONDS * m_sample_rate);

  int numSamples = data.maxFrameIndex * data.samplesPerFrame;

  data.recordedSamples = new float[numSamples];
  if( data.recordedSamples == NULL ) {
    printf("Could not allocate record buffer.\n");
    return false;
  }
  for(int i=0; i<numSamples; i++ ) {
    data.recordedSamples[i] = 0;
  }

  PaError err = 
    Pa_OpenStream(
                  &record_stream,
                  Pa_GetDefaultInputDeviceID(),
                  data.samplesPerFrame,
                  PA_SAMPLE_TYPE,
                  NULL,
                  paNoDevice,
                  0,
                  PA_SAMPLE_TYPE,
                  NULL,
                  m_sample_rate,
                  1024,            /* frames per buffer */
                  0,               /* number of buffers, if zero then use default minimum */
                  paClipOff,  /* we won't output out of range samples so don't bother clipping them */
                  recordCallback,
                  &data );
  if( err != paNoError )
  {
	  printf("Error opening record stream.\n");
	  return false;
  }

  err = Pa_StartStream( record_stream );
  if( err != paNoError )
  {
	  printf("Error starting record stream.\n");
	  return false;
  }

  return true;
}

bool recordStream::close() {
  return paNoError == Pa_CloseStream( record_stream );
}

bool processAudio(paRecData& data, int numSamples, double sample_rate,
				float& max, float& average_abs, float& bestpeak_freq)
{
  bool gotSound = false;

  //Measure amplitude (avg and peak)
  {
    float val;
    int i;

    max = 0;
    average_abs = 0;
    for(i=0; i<numSamples; i++)
	{
      val = data.recordedSamples[i];
      if(val < 0)
		  val = -val; //absolute value
      if(val > max)
		  max = val;
      average_abs += val;
    }
    average_abs /= numSamples;
  }

  float *autocorr = 0;
  int autocorrLen = 0;

  if((numSamples > 0) && (average_abs > 0))
  {
    //Autocorrelation - The highest peak is the pitch
    if(ComputeSpectrum(data.recordedSamples, numSamples,
						1024,&autocorr, autocorrLen))
	{
      //Find the tallest peak
      float bestpeak_x = (float) bestPeak(autocorr, autocorrLen, (float) sample_rate);
      if(bestpeak_x > 0)
	  {
        bestpeak_freq = (float) (sample_rate / bestpeak_x);

        if((bestpeak_freq > 1) && (bestpeak_freq < 20000))
		{
          gotSound = true;
        }
      }
    }
  }
  free(autocorr);
  return gotSound;
}

bool recordStream::audioInput(float &max, float &avg_abs, float& bestpeak_freq)
{
  int numSamplesSoFar = data.frameIndex * data.samplesPerFrame;

  bool success = processAudio(data,numSamplesSoFar,m_sample_rate,
								max, avg_abs, bestpeak_freq);
  data.frameIndex = 0;
  return success;
}
